/*--------------------------------------------------------------------------------------------------
Yatak Operation System Project Copyright 2012-2013
Author : Ilya Churaev
Started : 28 Feb 2013		Updated : 28 Feb 2013
File Name : net_buff.c		Purpose : Network packet buffer.
--------------------------------------------------------------------------------------------------*/
#include <yatak/yatak.h>
#include <yatak/kheap.h>
#include <net/netdev.h>
#include <keyboard.h>
#include <screen.h>
#include <sched.h>
#include <types.h>
#include <msg.h>

/* These are the defined Ethernet Protocol ID's. */
#define ETH_P_LOOP		0x0060		/* Ethernet Loopback packet*/
#define ETH_P_ECHO		0x0200		/* Ethernet Echo packet	*/
#define ETH_P_PUP		0x0400		/* Xerox PUP packet	*/
#define ETH_P_IP		0x0800		/* Internet Protocol packet*/
#define ETH_P_ARP		0x0806		/* Address Resolution packet*/
#define ETH_P_RARP		0x8035		/* Reverse Addr Res packet*/
#define ETH_P_X25		0x0805		/* CCITT X.25*/
#define ETH_P_IPX		0x8137		/* IPX over DIX*/
#define ETH_P_802_3		0x0001		/* Dummy type for 802.3 frames*/
#define ETH_P_AX25		0x0002		/* Dummy protocol id for AX.25*/
#define ETH_P_ALL		0x0003		/* Every packet (be careful!!!)*/

struct s_net_buff *net_buff = NULL;
extern int net_pid;

int init_net_buff(void)
{
	dprintf("net_buff.c: Initiailizing network packet buffering...\n");
	MessageFoS ("Initiailizing network packet buffering...\t\t\t",true);
	net_buff = NULL;
}

struct s_net_buff* net_buff_alloc(unsigned int size)
{
	struct s_net_buff *buff;
	buff = (struct s_net_buff*)kmalloc(sizeof(struct s_net_buff));
	memset(buff,0,sizeof(struct s_net_buff));
	buff->data = (unsigned char*)kmalloc(size+1);
	buff->data_length = size;
	
	return buff;
}

int net_buff_add(struct s_net_buff *buff)
{
	struct s_net_buff *p;
	struct ipc_message *m;
	unsigned int flags;
	
	save_flags(flags);
	cli();
	
	if ((net_buff) == NULL)               // first packet in queue
	{
		net_buff = buff;
		net_buff->next = NULL;
	}
	else                                  // insert at tail
	{
		p = net_buff;
		while (p->next)
			p = p->next;
		
		p->next = buff;
		p->next->next = NULL;
	}
	
	// send a message to protocol manager
	m = (struct ipc_message*)kmalloc(sizeof(struct ipc_message));
	m->type = MSG_NET_RECEIVED;
	
	send_msg(net_pid, m);
	kfree(m);
	
	restore_flags(flags);
}

struct s_net_buff *net_buff_get(void)
{
	struct s_net_buff *p;
	unsigned int flags;
	
	save_flags(flags);
	cli();
	
	if ((net_buff) == NULL)               // first packet in queue
	{
		restore_flags(flags);
		return NULL;
	}
	
	p = net_buff;
	net_buff = net_buff->next;
	
	restore_flags(flags);
	return p;
}

void net_buff_debug(void)
{
	struct s_net_buff *p;
	unsigned char ch=0;
	unsigned char str[255];
	
	cls();
	
	p = net_buff;
	
	if (!p)
	{
		kprintf("net_buff.c: net_buff_debug() no packets.");
		return;
	}
	
	while (ch!=27)
	{
		if (p)
		{
			sprintf(str, "Source Address     : %02X:%02X:%02X:%02X:%02X:%02X",p->eth->h_source[0],p->eth->h_source[1],
					p->eth->h_source[2],p->eth->h_source[3],p->eth->h_source[4],p->eth->h_source[5]);
			kprintf(str);
			
			sprintf(str, "Destination Address: %02X:%02X:%02X:%02X:%02X:%02X",p->eth->h_dest[0],p->eth->h_dest[1],
					p->eth->h_dest[2],p->eth->h_dest[3],p->eth->h_dest[4],p->eth->h_dest[5]);
			kprintf(str);
			switch (htons(p->eth->h_proto))
			{
				case ETH_P_LOOP	 : sprintf(str,"Ethernet Loopback packet"); break;
				case ETH_P_ECHO	 : sprintf(str,"Ethernet Echo packet"); break;
				case ETH_P_PUP 	 : sprintf(str,"Xerox PUP packet"); break;
				case ETH_P_IP  	 : sprintf(str,"Internet Protocol packet"); break;
				case ETH_P_ARP 	 : sprintf(str,"Address Resolution packet"); break;
				case ETH_P_RARP  : sprintf(str,"Reverse Address Resolution packet"); break;
				case ETH_P_X25 	 : sprintf(str,"CCITT X.25"); break;
				case ETH_P_IPX   : sprintf(str,"IPX over DIX"); break;
				case ETH_P_802_3 : sprintf(str,"Dummy type for 802.3 frames"); break;
				case ETH_P_AX25  : sprintf(str,"Dummy protocol ID for AX.25"); break;
				case ETH_P_ALL   : sprintf(str,"Every packet"); break;
				default: sprintf(str,"Unknown ethernet packet"); break;
			}
			kprintf("Ethernet type: %0004X %s",p->eth->h_proto,str);
			kprintf("Ethernet datas with header:");
			mem_dump(p->data, p->data_length);
			kprintf("");
			
			if (htons(p->eth->h_proto) == ETH_P_ARP)
			{
				arp_debug(p->data+sizeof(struct s_ethhdr));
			}
			kprintf("");
			ch = in_char(true);
			p = p->next;
		}
		else
			ch = 27;
	}
}